#pragma once
#include "inc_windows_h.hpp"
#include "inc_boost_io_ios_state.hpp"
#include "detail/basic_formatter.hpp"

namespace wxx {
namespace detail 
{
	template<class Ch, class Tr = std::char_traits<Ch>>
	struct parser_base : boost::noncopyable
	{
		typedef std::basic_istream<Ch, Tr> istream_type;
		
	protected:

		istream_type& m_input;

		explicit parser_base(istream_type& input) throw()
			: m_input(input)
		{
		}

		template<class T>
		void read_hex(T& result)
		{
			using namespace std;
			const istream_type::sentry ok(m_input);

			if (ok)
			{
				const streamsize charcount = 2 * sizeof(T);

				char buff[charcount] = { 0 };
				m_input.read(buff, charcount);

				boost::ulong_long_type n;
				std::stringstream(buff) >> std::hex >> n;

				if(n > std::numeric_limits<T>::max())
				{
					setfail();
					return;
				}

				result = static_cast<T>(n);
			}
		}

		bool read_if_eq(const char expected)
		{
			if (m_input)
			{
				if (m_input.peek() == expected)
				{
					Ch ch;
					m_input.read(&ch, 1);

					return true;
				}
			}
			
			return false;
		}

		void setfail()
		{
			m_input.setstate(std::ios_base::failbit);
		}
	};
	
	template<class Ch, class Tr = std::char_traits<Ch>>
	struct guid_parser : parser_base<Ch, Tr>
	{
		explicit guid_parser(istream_type& input)
			: parser_base(input)
		{
		}

		istream_type& read(::GUID& g)
		{
			using namespace std;
			using namespace boost::io;

			const typename istream_type::sentry ok(m_input);

			if(ok)
			{
				::GUID tmp;

				m_input >> std::hex;
				m_braces = read_if_eq('{');

				read_hex(tmp.Data1);
				m_hyphens = read_if_eq('-');

				read_hex(tmp.Data2);
				read_hyphen();

				read_hex(tmp.Data3);
				read_hyphen();
				
				for (int i = 0; i < 8; ++i)
				{
					if (i == 2 && m_hyphens)
					{
						read_hyphen();
					}

					read_hex(tmp.Data4[i]);
				}

				if (m_braces && !read_if_eq('}'))
				{
					setfail();
				}
				else
				{
					g = tmp;
				}
			}

			return m_input;
		}

	private:

		void read_hyphen()
		{
			if (m_hyphens)
			{
				if (!read_if_eq('-'))
				{
					setfail();
				}
			}
		}

		bool m_hyphens;
		bool m_braces;
	};

	template<class Ch, class Tr>
	struct guid_formatter : basic_formatter<::GUID, Ch, Tr>
	{
		explicit guid_formatter(const ::GUID& g)
			: basic_formatter(g)
		{
		}

		ostream_type& write_to(ostream_type& output) const
		{
			using namespace std;
			using namespace boost::io;

			ios_flags_saver flags_saver(output);
			ios_width_saver width_saver(output);
			basic_ios_fill_saver<Ch, Tr> fill_saver(output);

			const typename ostream_type::sentry ok(output);

			if (ok)
			{
				bool braces = !(guid::modifiers.noshowbraces(output));
				bool hyphens = !(guid::modifiers.noshowhyphens(output));

				if (braces) { output << '{'; }

				output.fill('0');
				output << std::hex;

				write_int(output, m_value.Data1, hyphens);
				write_int(output, m_value.Data2, hyphens);
				write_int(output, m_value.Data3, hyphens);

				for (int i = 0; i < 8; ++i)
				{
					write_int(output, m_value.Data4[i], false);
				}

				if (braces) { output << '}'; }
			}

			return output;
		}

	private:

		template<class T>
		void write_int(ostream_type& output, T value, bool hyphen = true) const
		{
			output.width(2 * sizeof(T));
			output << static_cast<unsigned int>(value);

			if (hyphen)
			{
				output << '-';
			}
		}
	};
}}